package com.vastcom.fabric.server.tools;

import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.ProposalException;
import org.hyperledger.fabric.sdk.exception.TransactionException;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

public class FabricClient {

    @Value("${fabric.orderer.name}")
    private String ordername;
    @Value("${fabric.orderer.grpcs.address}")
    private String ordergrpcaddress;


    public HFClient hfClient;

    Logger log= LoggerFactory.getLogger(FabricClient.class);


    public FabricClient     (UserContext userContext) {
        try {
            this.hfClient = HFClient.createNewInstance();
            CryptoSuite cryptoSuite=CryptoSuite.Factory.getCryptoSuite();
            hfClient.setCryptoSuite(cryptoSuite);
            hfClient.setUserContext(userContext);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (CryptoException e) {
            e.printStackTrace();
        } catch (InvalidArgumentException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    public Channel createChannel(String channelname, Orderer orderer, String txPath){
        try {
            ChannelConfiguration channelConfiguration=new ChannelConfiguration(new File(txPath));
            return hfClient.newChannel(channelname,orderer,
                    channelConfiguration,
                    hfClient.getChannelConfigurationSignature(channelConfiguration,
                            hfClient.getUserContext()));


        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidArgumentException e) {
            e.printStackTrace();
        } catch (TransactionException e) {
            e.printStackTrace();
        }
        return null;
    }


    public Channel getChannleByName(String channelname){
        //Channel channel =x.newChannel(channelname);
        try {
            Channel channel =hfClient.newChannel(channelname);
            if(channel!=null){
                //  dchannel.initialize();
                return channel;
            }
            return null;
        } catch (InvalidArgumentException  e) {
            e.printStackTrace();
            return null;
        }
    }



    /**
     * @description 获取orderer节点
     * @param name
     * @param grpcUrl
     * @param tlsFilePath
     * @return
     * @throws InvalidArgumentException
     */
    public Orderer getOrderer(String name,String grpcUrl,String tlsFilePath) throws InvalidArgumentException {
        Properties properties = new Properties();
        properties.setProperty("pemFile",tlsFilePath);
        Orderer orderer = hfClient.newOrderer(name,grpcUrl,properties);
        return orderer;
    }


    public Peer getPeer(String name, String grpcUrl, String tlsFilePath){
        try {
            Properties properties=new Properties();
            properties.setProperty("pemFile",tlsFilePath);
            Peer peer=hfClient.newPeer(name,grpcUrl,properties);
            return peer;
        } catch (InvalidArgumentException e) {
            e.printStackTrace();
            return null;
        }




    }


  public   Orderer getOrder(){

      try {
          String tlscapath=FileTools.CRYPTO_PATH+FileTools.ORDERER_TLS_FILE;
          Orderer temp=this.getOrderer("orderer.example.com",
                  "grpcs://orderer.example.com:7050",
                  tlscapath
          );
          return temp;
      } catch (InvalidArgumentException e) {
          e.printStackTrace();
      }

    return null;
  }



    public Peer getPeerByName(String name) {

        Peer result=null;
        String tlsfile=FileTools.CRYPTO_PATH;
        switch (name){
            case "Org1":
               /* tlsfile+="peerOrganizations/rawmaterial.4media.video/msp/tlscacerts/tlsca.rawmaterial.4media.video-cert.pem";
                result=this.getPeer("peer0.rawmaterial.4media.video",
                        "grpcs://peer0.rawmaterial.4media.video:7051",
                        tlsfile);
                         */
                tlsfile+="peerOrganizations/org1.example.com/msp/tlscacerts/tlsca.org1.example.com-cert.pem";
                result=this.getPeer("peer0.org1.example.com",
                        "grpcs://peer0.org1.example.com:7051",
                        tlsfile);
                break;
            case "rawmateria_peer1":
                tlsfile+="peerOrganizations/rawmaterial.4media.video/msp/tlscacerts/tlsca.rawmaterial.4media.video-cert.pem";
                result=this.getPeer("peer1.rawmaterial.4media.video",
                        "grpcs://peer1.rawmaterial.4media.video:8051",
                        tlsfile);
                break;
            case "bake_p1":
                tlsfile="peerOrganizations/baker.4media.video/msp/tlscacerts/tlsca.baker.4media.video-cert.pem";
                result=this.getPeer("peer0.baker.4media.video",
                        "grpcs://peer0.baker.4media.video:9051",
                        tlsfile);


                break;

            case "back_p2":
                tlsfile="peerOrganizations/baker.4media.video/msp/tlscacerts/tlsca.baker.4media.video-cert.pem";
                result=this.getPeer("peer1.baker.4media.video",
                        "grpcs://peer1.baker.4media.video:10051",
                        tlsfile);
                break;





        }



        return result;
    }



    public HashMap queryChaincode(List<Peer> peers, String channelName, TransactionRequest.Type lang, String chaincodeName, String funcName, String args[]){
        HashMap result=new HashMap();
        try {
            Channel channel=this.getChannleByName(channelName);
            for(Peer p:peers){
                channel.addPeer(p);
            }
            channel.initialize();
            QueryByChaincodeRequest queryByChaincodeRequest=hfClient.newQueryProposalRequest();
            ChaincodeID.Builder builder = ChaincodeID.newBuilder().setName(chaincodeName);
            queryByChaincodeRequest.setChaincodeID(builder.build());
            queryByChaincodeRequest.setArgs(args);
            queryByChaincodeRequest.setFcn(funcName);
            queryByChaincodeRequest.setChaincodeLanguage(lang);
            Collection<ProposalResponse> responses=channel.queryByChaincode(queryByChaincodeRequest);
            for (ProposalResponse response : responses) {
                if (response.getStatus().getStatus() == 200) {
                    log.info("data is {}", response.getProposalResponse().getResponse().getPayload());
                   log.warn("result is"+response.toString());

//                    result.put("chain",response.getProposalResponse().getResponse().toString());
                   //byte[] bytes= response.getProposalResponse().getResponse().getPayload();

                    result.put("result",new String(response.getProposalResponse().getResponse().getPayload().toByteArray()));

                    return result;
                } else {
                    log.error("data get error {}", response.getMessage());
                    result.put(response.getStatus().getStatus(),response.getMessage());
                    return result;
                }
            }
            result.put("code","404");
            return result;



        } catch (InvalidArgumentException | TransactionException | ProposalException e) {
            e.printStackTrace();
        }
        result.put("msg","error");
        return result;
    }



    public boolean invoke(String channelName, TransactionRequest.Type lang, String chaincodeName, Orderer order, List<Peer> peers, String funcName, String args[])
    {
        try {
            Channel channel = this.getChannleByName(channelName);
            channel.addOrderer(order);
            for(Peer p : peers) {
                channel.addPeer(p);
            }
            channel.initialize();
            TransactionProposalRequest transactionProposalRequest = hfClient.newTransactionProposalRequest();
            transactionProposalRequest.setChaincodeLanguage(lang);
            transactionProposalRequest.setArgs(args);
            transactionProposalRequest.setFcn(funcName);
            ChaincodeID.Builder builder = ChaincodeID.newBuilder().setName(chaincodeName);
            transactionProposalRequest.setChaincodeID(builder.build());
            Collection<ProposalResponse> responses = channel.sendTransactionProposal(transactionProposalRequest,peers);
            for(ProposalResponse response:responses){
                if(response.getStatus().getStatus()==200){
                    log.info("{} invoke proposal {} sucess",response.getPeer().getName(),funcName);
                }else{
                    String logArgs[] = {response.getMessage(),funcName,response.getPeer().getName()};
                    log.error("{} invoke proposal {} fail on {}",logArgs);
                }
            }
            channel.sendTransaction(responses);
            return true;
        } catch (InvalidArgumentException e) {
            e.printStackTrace();
        } catch (TransactionException e) {
            e.printStackTrace();
        } catch (ProposalException e) {
            e.printStackTrace();
        }
        return false;
    }




}
